package org.javaboy.jwt_redis_demo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.javaboy.jwt_redis_demo.model.RespBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import java.io.PrintWriter;
import java.time.Duration;

//@Component
public class JwtFilter extends GenericFilterBean {
//    @Autowired
    StringRedisTemplate redisTemplate;

    public JwtFilter(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
        RespBean result = null;
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        resp.setContentType("application/json;charset=utf-8");
        try {
            //如果是登录请求，则直接放行
            if (req.getRequestURI().equals("/login")) {
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            }
            //获取请求头中的token
            String token = req.getHeader("Authorization");
            if (token == null) {
                result = new RespBean(500, "令牌为空，请求失败", null);
                PrintWriter out = resp.getWriter();
                out.write(new ObjectMapper().writeValueAsString(result));
            } else {
                //验证token
                token = token.replace("Bearer ", "");
                //验证token是否有效
                //校验通过，则返回认证后的 Auhtentication 对象，否则返回 null
                Authentication authentication = JwtUtils.verifyToken(token);
                String s = redisTemplate.opsForValue().get(authentication.getName() + ":" + token);
                if (s == null) {
                    // 令牌失效
                    result = new RespBean(501, "令牌失效，请重新登录", null);
                    resp.getWriter().write(new ObjectMapper().writeValueAsString(result));
                } else {
                    //令牌有效，检查令牌过期时间，如果临近过期，则为令牌续期
                    Long expire = redisTemplate.getExpire(authentication.getName() + ":" + token);
                    if (expire != null && expire < 10 * 60) {
                        //说明令牌还有 10 分钟过期，则重新设置新的过期时间
                        redisTemplate.expire(authentication.getName() + ":" + token, Duration.ofSeconds(JwtUtils.REDIS_TOKEN_EXPIRE_TIME));
                    }
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    filterChain.doFilter(servletRequest, servletResponse);
                }
            }
        } catch (Exception e) {

        } finally {
            // 清除上下文
            SecurityContextHolder.clearContext();
        }

    }
}
